/*
 *  T3::Scalar.cpp
 *  T3nsors
 *
 *  Created by Michael Barriault on 10-09-23.
 *  Copyright 2010 MikBarr Studios. All rights reserved.
 *
 */

#include <T3nsors/Scalar.h>
#include <T3nsors/Macros.h>
#include <T3nsors/Others.h>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <cstdarg>

T3::Scalar::Scalar(void) {
	return;
}

T3::Scalar::Scalar(std::string iid) {
	*this = T3::Scalar(Domain(1,0,0.,0.),iid);
}

T3::Scalar::Scalar(T3::Domain iO, std::string iid) {
	O = iO;
	id = iid;
	T.assign(O(), 0.0);
}

double& T3::Scalar::operator[](int i) {
	if (!(T[i] == T[i])) {
		printf("\nGot a NaN here, item %d of object %s\n", i, id.c_str());
		exit(-1);
	}
	if (O() == 1)
		return T[0];
	return T[i];
}

double& T3::Scalar::operator()(int a, ...) {
	va_list va;
	va_start(va, a);
	int t=0;
	FOR(i,O.N) {
		int A = (a>=0) ? a : O(i)+a;
		for ( int j=i-1; j>=0; j-- )
			A *= O(j);
		t += A;
		a = va_arg(va, int);
	}
	va_end(va);
	return T[t];
}

T3::Scalar& T3::Scalar::operator=(double x) {
	FORO T[o] = x;
	return *this;
}

T3::Scalar& T3::Scalar::operator+=(T3::Scalar& x) {
	CheckDims(O, x.O);
	FORO T[o] += x[o];
	return *this;
}

T3::Scalar operator-(T3::Scalar x) {
	T3::Scalar y(x.O,(std::string)"-"+x.id);
	FOR(o,x.O()) y[o] = -x[o];
	return y;
}

T3::Scalar operator+(T3::Scalar x, T3::Scalar y) {
	T3::Domain O = Merge(x.O,y.O);
	T3::Scalar z(O,x.id+"+"+y.id);
	FORO z[o] = x[o] + y[o];
	return z;
}

T3::Scalar operator-(T3::Scalar x, T3::Scalar y) {
	T3::Domain O = Merge(x.O,y.O);
	T3::Scalar z(O,x.id+"-"+y.id);
	FORO z[o] = x[o] - y[o];
	return z;
}

T3::Scalar operator*(T3::Scalar x, T3::Scalar y) {
	T3::Domain O = Merge(x.O,y.O);
	T3::Scalar z(O,x.id+"*"+y.id);
	FORO z[o] = x[o] * y[o];
	return z;
}

T3::Scalar operator/(T3::Scalar x, T3::Scalar y) {
	T3::Domain O = Merge(x.O,y.O);
	T3::Scalar z(O,x.id+"/"+y.id);
	FORO z[o] = x[o] / y[o];
	return z;
}

T3::Scalar operator^(T3::Scalar x, T3::Scalar y) {
	T3::Domain O = Merge(x.O,y.O);
	T3::Scalar z(O,x.id+"^"+y.id);
	FORO z[o] = pow(x[o], y[o]);
	return z;
}

T3::Scalar operator+(T3::Scalar x, double y) {
	T3::Scalar z(x.O,x.id+"+"+ftos(y));
	FOR(o,x.O()) z[o] = x[o] + y;
	return z;
}

T3::Scalar operator-(T3::Scalar x, double y) {
	T3::Scalar z(x.O,x.id+"-"+ftos(y));
	FOR(o,x.O()) z[o] = x[o] - y;
	return z;
}

T3::Scalar operator*(T3::Scalar x, double y) {
	T3::Scalar z(x.O,x.id+"*"+ftos(y));
	FOR(o,x.O()) z[o] = x[o] * y;
	return z;
}

T3::Scalar operator/(T3::Scalar x, double y) {
	T3::Scalar z(x.O,x.id+"/"+ftos(y));
	FOR(o,x.O())
	z[o] = x[o] / y;
	return z;
}

T3::Scalar operator^(T3::Scalar x, double y) {
	T3::Scalar z(x.O,x.id+"^"+ftos(y));
	FOR(o,x.O()) z[o] = pow(x[o] , y);
	return z;
}

T3::Scalar operator+(double x, T3::Scalar y) {
	return y+x;
}

T3::Scalar operator-(double x, T3::Scalar y) {
	return -(y-x);
}

T3::Scalar operator*(double x, T3::Scalar y) {
	return y*x;
}

T3::Scalar operator/(double x, T3::Scalar y) {
	T3::Scalar z(y.O,ftos(x)+"/"+y.id);
	FOR(o,y.O()) z[o] = x / y[o];
	return z;
}

T3::Scalar operator^(double x, T3::Scalar y) {
	T3::Scalar z(y.O,ftos(x)+"^"+y.id);
	FOR(o,y.O()) z[o] = pow(x , y[o]);
	return z;
}

T3::Scalar::operator double() {
	return T[0];
}

T3::Scalar sin(T3::Scalar x) {
	T3::Scalar y(x.O,(string)"sin"+x.id);
	FOR(o,x.O()) y[o] = sin(x[o]);
	return y;
}

T3::Scalar cos(T3::Scalar x) {
	T3::Scalar y(x.O,(string)"cos"+x.id);
	FOR(o,x.O()) y[o] = cos(x[o]);
	return y;
}

T3::Scalar sin2(T3::Scalar x) {
	T3::Scalar y(x.O,(string)"sin2"+x.id);
	FOR(o,x.O()) y[o] = pow(sin(x[o]),2.);
	return y;
}

T3::Scalar sqrt(T3::Scalar x) {
	T3::Scalar y(x.O,(string)"sqrt"+x.id);
	FOR(o,x.O()) y[o] = sqrt(fabs(x[o]));
	return y;
}

T3::Scalar log(T3::Scalar x, double b) {
	T3::Scalar y(x.O,(string)"log_"+ftos(b)+" "+x.id);
	FOR(o,x.O()) y[o] = log(x[o])/log(b);
	return y;
}

double l2(T3::Scalar x, double p) {
	double l2x = 0;
	FOR(o,x.O()) l2x += pow(x[o],p);
	l2x *= pow(x.O.d(), x.O.N);
	FOR(n,x.O.N) l2x /= (x.O.max(n)-x.O.min(n));
	l2x = pow(fabs(l2x),1./p);
	return l2x;
}

T3::Scalar LinCom(double a, T3::Scalar w, double b, T3::Scalar x, std::string id) {
	T3::Domain O = Merge(w.O,x.O);
	T3::Scalar v(O, id);
	FORO v[o] = a*w[o]+b*x[o];
	return v;
}

T3::Scalar LinCom(double a, T3::Scalar w, double b, T3::Scalar x, double c, T3::Scalar y, std::string id) {
	T3::Domain O = Merge(Merge(w.O,x.O),y.O);
	T3::Scalar v(O, id);
	FORO v[o] = a*w[o]+b*x[o]+c*y[o];
	return v;
}

T3::Scalar LinCom(double a, T3::Scalar w, double b, T3::Scalar x, double c, T3::Scalar y, double d, T3::Scalar z, std::string id) {
	T3::Domain O = Merge(Merge(Merge(w.O,x.O),y.O),z.O);
	T3::Scalar v(O, id);
	FORO v[o] = a*w[o]+b*x[o]+c*y[o]+d*z[o];
	return v;
}

